package gui;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import app.AppInterface;

@SuppressWarnings("serial")
public class GuiConfig extends JPanel {

	private final boolean DEBUG = false;

	private AppInterface app;
	private GuiObserver mainGui;
	private JRadioButton dualwayButton, outgoingButton, incomingButton, backupButton;
	private ButtonGroup syncModeButtons;
	private JLabel sendPortLabel, listenPortLabel, syncHoursLabel, syncMinutesLabel;
	private JTextField sendPortField, listenPortField;
	private JSpinner syncHour, syncMinute;
	private SpinnerNumberModel syncHours, syncMinutes;
	private JCheckBox autoSyncCheckBox, allowDeleteCheckBox, deleteWarningCheckBox, includeHiddenCheckBox, useExactTimeCheckBox;
	private JButton saveConfigButton;

	public GuiConfig(AppInterface app, GuiObserver mainGui) {

		this.app = app;
		this.mainGui = mainGui;
		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));


		// Radio Button Panel
		JPanel radioPanel = new JPanel();
		radioPanel.setLayout(new BoxLayout(radioPanel, BoxLayout.Y_AXIS));
		radioPanel.setAlignmentX((float)0.5);

		dualwayButton = new JRadioButton(" Dual-Way Sync");
		dualwayButton.setMnemonic('D');
		dualwayButton.setToolTipText("This mode will make both computer try to update their folders");
		outgoingButton = new JRadioButton(" Out-Going Sync");
		outgoingButton.setMnemonic('O');
		outgoingButton.setToolTipText("This mode will make this computer try to send updated files to the other computer");
		incomingButton = new JRadioButton(" In-Coming Sync");
		incomingButton.setMnemonic('I');
		incomingButton.setToolTipText("This mode will force the other computer to send files that are updated to this computer");
		backupButton = new JRadioButton(" Backup Mode");
		backupButton.setMnemonic('B');
		backupButton.setToolTipText("This mode will force the other computer to make its folder look like this computers folder");
		dualwayButton.setActionCommand("dual");
		outgoingButton.setActionCommand("out");
		incomingButton.setActionCommand("in");
		backupButton.setActionCommand("backup");

		syncModeButtons = new ButtonGroup();
		syncModeButtons.add(dualwayButton);
		syncModeButtons.add(outgoingButton);
		syncModeButtons.add(incomingButton);
		syncModeButtons.add(backupButton);

		SyncModeListener syncModeListener = new SyncModeListener();
		dualwayButton.addActionListener(syncModeListener);
		outgoingButton.addActionListener(syncModeListener);
		incomingButton.addActionListener(syncModeListener);
		backupButton.addActionListener(syncModeListener);

		radioPanel.add(dualwayButton);
		radioPanel.add(outgoingButton);
		radioPanel.add(incomingButton);
		radioPanel.add(backupButton);
		radioPanel.setBorder(new TitledBorder("Sync Mode"));


		// Port Panel
		JPanel portPanel = new JPanel();

		sendPortLabel = new JLabel("Send Port:");
		sendPortField = new JTextField(5);
		SendPortListener sendPortListener = new SendPortListener();
		sendPortField.addActionListener(sendPortListener);
		sendPortField.addFocusListener(sendPortListener);
		portPanel.add(sendPortLabel);
		portPanel.add(sendPortField);

		listenPortLabel = new JLabel("Listen Port:");
		listenPortField = new JTextField(5);
		listenPortField.setToolTipText("This field can only be modified when you are not listening");
		ListenPortListener listenPortListener = new ListenPortListener();
		listenPortField.addActionListener(listenPortListener);
		listenPortField.addFocusListener(listenPortListener);
		portPanel.add(Box.createRigidArea(new Dimension(5, 0)));
		portPanel.add(listenPortLabel);
		portPanel.add(listenPortField);


		// Sync Delay Panel
		JPanel syncDelayPanel = new JPanel();

		autoSyncCheckBox = new JCheckBox(" Auto Sync");
		autoSyncCheckBox.setMnemonic('A'); 
		AutoSyncListener autoSyncListener = new AutoSyncListener();
		autoSyncCheckBox.addItemListener(autoSyncListener);

		syncHoursLabel = new JLabel("Hours:");
		syncMinutesLabel = new JLabel("Minutes:");
		syncHours = new SpinnerNumberModel(0, 0, 24, 1);
		syncMinutes = new SpinnerNumberModel(10, 0, 59, 1);
		syncHour = new JSpinner(syncHours);
		syncMinute = new JSpinner(syncMinutes);
		AutoSyncDelayListener autoSyncDelayListener = new AutoSyncDelayListener();
		syncHours.addChangeListener(autoSyncDelayListener);
		syncMinutes.addChangeListener(autoSyncDelayListener);

		syncDelayPanel.add(autoSyncCheckBox);
		syncDelayPanel.add(Box.createRigidArea(new Dimension(5, 0)));
		syncDelayPanel.add(syncHoursLabel);
		syncDelayPanel.add(syncHour);
		syncDelayPanel.add(Box.createRigidArea(new Dimension(5, 0)));
		syncDelayPanel.add(syncMinutesLabel);
		syncDelayPanel.add(syncMinute);


		// Delete CheckBox Panel
		JPanel deleteCheckBoxPanel = new JPanel();
		deleteCheckBoxPanel.setLayout(new BoxLayout(deleteCheckBoxPanel, BoxLayout.X_AXIS));
		deleteCheckBoxPanel.setAlignmentX((float)0.5);
		allowDeleteCheckBox = new JCheckBox(" Allow Delete");
		allowDeleteCheckBox.setMnemonic('l');
		AllowDeleteListener allowDeleteListener = new AllowDeleteListener();
		allowDeleteCheckBox.addItemListener(allowDeleteListener);
		deleteCheckBoxPanel.add(allowDeleteCheckBox);

		deleteWarningCheckBox = new JCheckBox(" Warning (Delete)");
		deleteWarningCheckBox.setMnemonic('W');
		deleteWarningCheckBox.setEnabled(false);
		DeleteWarningListener deleteWaringListener = new DeleteWarningListener();
		deleteWarningCheckBox.addItemListener(deleteWaringListener);
		deleteCheckBoxPanel.add(deleteWarningCheckBox);


		// CheckBox Panel
		JPanel checkBoxPanel = new JPanel();
		checkBoxPanel.setLayout(new BoxLayout(checkBoxPanel, BoxLayout.X_AXIS));
		checkBoxPanel.setAlignmentX((float)0.5);
		includeHiddenCheckBox = new JCheckBox(" Include Hidden Files");
		includeHiddenCheckBox.setMnemonic('H');
		IncludeHiddenListener includeHiddenListener = new IncludeHiddenListener();
		includeHiddenCheckBox.addItemListener(includeHiddenListener);
		checkBoxPanel.add(includeHiddenCheckBox);

		useExactTimeCheckBox = new JCheckBox(" Use Exact Time");
		useExactTimeCheckBox.setMnemonic('T');
		useExactTimeCheckBox.setToolTipText("It is recommended to have this check box unticked if you are syncing between different file systems.\n" +
		"This is because some file systems don't store the files modification time with the same accuracy.");
		UseExactTimeListener useExactTimeListener = new UseExactTimeListener();
		useExactTimeCheckBox.addItemListener(useExactTimeListener);
		checkBoxPanel.add(useExactTimeCheckBox);


		// Save button
		saveConfigButton = new JButton("Save Config");
		saveConfigButton.setMnemonic('S');
		saveConfigButton.setAlignmentX((float)0.5);
		SaveConfigButtonListener saveConfigButtonListener = new SaveConfigButtonListener();
		saveConfigButton.addActionListener(saveConfigButtonListener);


		// Adds the panels to the GuiConfig panel
		add(Box.createRigidArea(new Dimension(0, 5)));
		add(radioPanel);
		add(Box.createRigidArea(new Dimension(0, 20)));
		add(portPanel);
		add(Box.createRigidArea(new Dimension(0, 1)));
		add(syncDelayPanel);
		add(Box.createRigidArea(new Dimension(0, 1)));
		add(deleteCheckBoxPanel);
		add(Box.createRigidArea(new Dimension(0, 15)));
		add(checkBoxPanel);
		add(Box.createRigidArea(new Dimension(0, 30)));
		//add(Box.createVerticalGlue());
		add(saveConfigButton);
		add(Box.createRigidArea(new Dimension(0, 5)));
	}

	// Is called to initiated the config tab
	public void setGuiConf() {

		if(DEBUG) System.out.println("Initialize gui conf");

		deleteWarningCheckBox.setSelected(app.getDeleteWarning());
		allowDeleteCheckBox.setSelected(app.getAllowDelete());
		includeHiddenCheckBox.setSelected(app.getIncludeHidden());
		useExactTimeCheckBox.setSelected(app.getUseExactTime());

		String syncMode = app.getSyncMode();
		if(syncMode.equals("dual")) {
			dualwayButton.setSelected(true);
		}
		else if(syncMode.equals("out")) {
			outgoingButton.setSelected(true);
		}
		else if(syncMode.equals("in")) {
			incomingButton.setSelected(true);
		}
		else {
			backupButton.setSelected(true);
		}

		sendPortField.setText("" + app.getSendPort());
		listenPortField.setText("" + app.getListenPort());


		int delay = app.getAutoSyncDelay()/1000;
		int hour = 0;
		while(delay >= 3600) {
			delay -= 3600;
			hour++;
		}
		syncHours.setValue(hour);
		int min = 0;
		while(delay >= 60) {
			delay -= 60;
			min++;
		}
		syncMinutes.setValue(min);

		autoSyncCheckBox.setSelected(app.getAutoSync());
		if(!app.getStartListen() && !app.getLocalSync()) {
			autoSyncCheckBox.setEnabled(false);
		}
	}

	public void showListenPortLabel(boolean show) {
		listenPortLabel.setEnabled(show);
	}

	public void showListenPort(boolean show) {
		listenPortField.setEnabled(show);
	}

	public void showAutoSync(boolean show) {
		autoSyncCheckBox.setEnabled(show);
	}

	public void setAutoSyncStatus(boolean status) {
		autoSyncCheckBox.setSelected(status);
	}


	private class SyncModeListener implements ActionListener {

		public void actionPerformed(ActionEvent event) {
			app.setSyncMode(event.getActionCommand());
		}
	}

	private class SendPortListener implements ActionListener, FocusListener {

		public void setSendPort() {

			int sendPort = app.getSendPort();
			try {
				sendPort = Integer.parseInt( sendPortField.getText() );
			} catch(Exception e) { // If the text was not possible to convert to an int
				sendPortField.setText("" + sendPort);
				e.printStackTrace();
			}
			if(sendPort <= 0) { // Checks if the port is below 1
				sendPort = 1;
				sendPortField.setText("" + sendPort);
			}
			else if(sendPort > 65535) { // Checks if the port is above 65535
				sendPort = 65535;
				sendPortField.setText("" + sendPort);
			}
			app.setSendPort(sendPort);
		}

		public void actionPerformed(ActionEvent event) {
			setSendPort();
		}

		public void focusLost(FocusEvent event) {
			setSendPort();
		}

		public void focusGained(FocusEvent event) {
		}
	}

	private class ListenPortListener implements ActionListener, FocusListener {

		public void setListenPort() {

			int listenPort = app.getListenPort();
			try {
				listenPort = Integer.parseInt( listenPortField.getText() );
			} catch(Exception e) { // If the text was not possible to convert to an int
				listenPortField.setText("" + listenPort);
				e.printStackTrace();
			}
			if(listenPort <= 0) { // Checks if the port is below 1
				listenPort = 1;
				listenPortField.setText("" + listenPort);
			}
			else if(listenPort > 65535) { // Checks if the port is above 65535
				listenPort = 65535;
				listenPortField.setText("" + listenPort);
			}
			app.setListenPort(listenPort);
		}

		public void actionPerformed(ActionEvent event) {
			setListenPort();
		}

		public void focusLost(FocusEvent event) {
			setListenPort();
		}

		public void focusGained(FocusEvent event) {
		}
	}

	private class AutoSyncListener implements ItemListener {

		public void itemStateChanged(ItemEvent event) {

			if(autoSyncCheckBox.isSelected()) {
				if(app.getAutoSyncDelay() > 0) {
					if(DEBUG) System.out.println("Auto Sync");
					app.setAutoSync(true);
					mainGui.setAutoSync(true);
				}
				else {
					autoSyncCheckBox.setSelected(false);
				}
			}
			else {
				if(DEBUG) System.out.println("Do not Auto Sync");
				app.setAutoSync(false);
				mainGui.setAutoSync(false);
			}
		}
	}

	private class AutoSyncDelayListener implements ChangeListener {

		public void stateChanged(ChangeEvent event) {

			int delay = 0;
			delay += ( syncMinutes.getNumber().intValue() * 60000 );
			delay += ( syncHours.getNumber().intValue() * 3600000 );

			app.setAutoSyncDelay(delay);

			if(delay > 0) {
				if(autoSyncCheckBox.isSelected()) { // restarts the timer with the new delay time
					app.setAutoSync(false);
					mainGui.setAutoSync(false);
					app.setAutoSync(true);
					mainGui.setAutoSync(true);
				}
			}
			else { // stops autoSync if the delay time is <= 0
				autoSyncCheckBox.setSelected(false);
			}
		}
	}

	private class AllowDeleteListener implements ItemListener {

		public void itemStateChanged(ItemEvent event) {

			if(dualwayButton.isSelected() && !app.getLocalSync()) {
				JOptionPane.showMessageDialog(null, 
						"If you are running on:\n\t-Dual-Way mode, and if\n" +
						"\t-allow delete is only selected on one computer\n\n" +
						"The outcome of the sync will be unpredictable in case of\n" +
						"a deletion depending on which computer initiates the sync!", 
						"Sync IT - Warning", JOptionPane.WARNING_MESSAGE);
			}

			if(allowDeleteCheckBox.isSelected()) {
				deleteWarningCheckBox.setEnabled(true);
				app.setAllowDelete(true);
			}
			else {
				deleteWarningCheckBox.setEnabled(false);
				app.setAllowDelete(false);
			}
		}
	}

	private class DeleteWarningListener implements ItemListener {

		public void itemStateChanged(ItemEvent event) {
			app.setDeleteWarning(deleteWarningCheckBox.isSelected());
		}
	}

	private class IncludeHiddenListener implements ItemListener {

		public void itemStateChanged(ItemEvent event) {
			app.setIncludeHidden(includeHiddenCheckBox.isSelected());
		}
	}

	private class UseExactTimeListener implements ItemListener {

		public void itemStateChanged(ItemEvent event) {
			app.setUseExactTime(useExactTimeCheckBox.isSelected());
		}
	}

	private class SaveConfigButtonListener implements ActionListener {

		public void actionPerformed(ActionEvent event) {

			app.saveConfig();
		}
	}



	// Methods that comes from GuiChangeObserver, sent through the mainGui //

	// Enables and disables components when a sync is initiated or finished
	// is called through an observer
	public void enableGuiComponents(boolean show) {

		dualwayButton.setEnabled(show);
		outgoingButton.setEnabled(show);
		incomingButton.setEnabled(show);
		backupButton.setEnabled(show);
		sendPortLabel.setEnabled(show);
		sendPortField.setEnabled(show);
		syncHoursLabel.setEnabled(show);
		syncHour.setEnabled(show);
		syncMinutesLabel.setEnabled(show);
		syncMinute.setEnabled(show);
		allowDeleteCheckBox.setEnabled(show);
		includeHiddenCheckBox.setEnabled(show);
		useExactTimeCheckBox.setEnabled(show);
		saveConfigButton.setEnabled(show);

		if(!show) { // disable component
			deleteWarningCheckBox.setEnabled(false);
			autoSyncCheckBox.setEnabled(false);
		}
		else { // enable component
			if(allowDeleteCheckBox.isSelected()) {
				deleteWarningCheckBox.setEnabled(true);
			}
			if( app.getStartListen() || app.getLocalSync() ) {
				autoSyncCheckBox.setEnabled(true);
			}
		}
	}


	// Methods that comes from ConfigChangeObserver, sent through the mainGui //

	public void changeConfigSyncMode(String syncMode) {
		// Handle sync mode change
		if(syncMode.equals("dual")) {
			dualwayButton.setSelected(true);
		}
		else if(syncMode.equals("out")) {
			outgoingButton.setSelected(true);
		}
		else if(syncMode.equals("in")) {
			incomingButton.setSelected(true);
		}
		else {
			backupButton.setSelected(true);
		}
	}

	public void changeConfigIncludeHidden(boolean includeHidden) {
		// Handle hidden mode change
		includeHiddenCheckBox.setSelected(includeHidden);
		// TODO the settings should be changed in the config by the configChangeObserver and not by the ItemListener
	}

	public void changeConfigUseExactTime(boolean useExactTime) {
		useExactTimeCheckBox.setSelected(useExactTime);
		// TODO the settings should be changed in the config by the configChangeObserver and not by the ItemListener
	}

}
